use super::super::ggg::*;
use super::*;

#[derive(Debug, Clone)]
pub struct Table<'a> {
	pub major_version: u16, // Major version of the BASE table
	pub minor_version: u16, // Minor version of the BASE table
	// Offset16	horizAxisOffset	// Offset to horizontal Axis table, from beginning of BASE table (may be NULL).
	pub horiz_axis: Option<Axis<'a>>,
	// Offset16	vertAxisOffset	// Offset to vertical Axis table, from beginning of BASE table (may be NULL).
	pub vert_axis: Option<Axis<'a>>,
	pub item_var_store_offset: Option<Offset32>, // Offset to ItemVariationStore table, from beginning of BASE table (may be null).
}
impl<'a> FromSlice<'a> for Table<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			major_version: s.read()?,
			minor_version: s.read()?,
			horiz_axis: s.parse_at_offset16(data),
			vert_axis: s.parse_at_offset16(data),
			item_var_store_offset: s.read()?,
		})
	}
}

#[derive(Debug, Clone)]
pub struct Axis<'a> {
	// Offset16	baseTagListOffset	// Offset to BaseTagList table, from beginning of Axis table (may be NULL).
	pub base_tag_list: LazyArray16<'a, Tag>,
	// Offset16	baseScriptListOffset	// Offset to BaseScriptList table, from beginning of Axis table.
	pub base_script_list: BaseScriptList<'a>,
}
impl<'a> FromSlice<'a> for Axis<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			base_tag_list: s.parse_array16()?,
			base_script_list: BaseScriptList::parse(s.tail()?)?,
		})
	}
}

pub type BaseScriptList<'a> = RecordList<'a, BaseScript<'a>>;
// BaseScriptList
// uint16	baseScriptCount	Number of BaseScriptRecords defined.
// BaseScriptRecord	baseScriptRecords[baseScriptCount]	Array of BaseScriptRecords, in alphabetical order by baseScriptTag.

// BaseScriptRecord
// Tag	baseScriptTag	4-byte script identification tag.
// Offset16	baseScriptOffset	Offset to BaseScript table, from beginning of BaseScriptList.

#[derive(Debug, Clone)]
pub struct BaseScript<'a> {
	pub tag: Tag,
	// Offset16	baseValuesOffset:	// Offset to BaseValues table, from beginning of BaseScript table (may be NULL).
	pub base_value: Option<BaseValues<'a>>,
	// Offset16	defaultMinMaxOffset	// Offset to MinMax table, from beginning of BaseScript table (may be NULL).
	pub default_minmax: Option<MinMax<'a>>,
	// uint16	baseLangSysCount	// Number of BaseLangSys records defined — may be zero (0).
	// BaseLangSys	baseLangSysRecords[baseLangSysCount]	// Array of BaseLangSys records, in alphabetical order by BaseLangSysTag.
	pub records: MinMaxList<'a>,
}
impl<'a> RecordListItem<'a> for BaseScript<'a> {
	fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			tag,
			base_value: s.parse_at_offset16(data),
			default_minmax: s.read_at_offset16(data).and_then(|d| MinMax::parse(Tag::from_bytes(b"dlft"), d)),
			records: MinMaxList::parse(s.tail()?)?,
		})
	}
}

#[derive(Debug, Clone)]
pub struct BaseValues<'a> {
	pub default_index: u16, // Index number of default baseline for this script — equals index position of baseline tag in baselineTags array of the BaseTagList.
	// uint16	baseCoordCount	// Number of BaseCoord tables defined — should equal baseTagCount in the BaseTagList.
	// Offset16	baseCoordOffsets[baseCoordCount]	// Array of offsets to BaseCoord tables, from beginning of BaseValues table — order matches baselineTags array in the BaseTagList.
	pub base_coords: LazyOffsetArray16<'a, BaseCoord<'a>>,
}
impl<'a> FromSlice<'a> for BaseValues<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			default_index: s.read()?,
			base_coords: LazyOffsetArray16::new(data, s.parse_array16()?),
		})
	}
}

pub type MinMaxList<'a> = RecordList<'a, MinMax<'a>>;

#[derive(Clone, Copy)]
pub struct MinMax<'a> {
	pub tag: Tag,
	// Offset16	minCoordOffset	Offset to BaseCoord table that defines the minimum extent value, from the beginning of MinMax table (may be NULL).
	pub min_coord: Option<BaseCoord<'a>>,
	// Offset16	maxCoordOffset	Offset to BaseCoord table that defines maximum extent value, from the beginning of MinMax table (may be NULL).
	pub max_coord: Option<BaseCoord<'a>>,
	// uint16	featMinMaxCount	Number of FeatMinMaxRecords — may be zero (0).
	// FeatMinMax	featMinMaxRecords[featMinMaxCount]	Array of FeatMinMax records, in alphabetical order by featureTag.
	pub feat_min_max: LazyArray16<'a, FeatMinMax>,
	pub data: &'a [u8],
}
impl<'a> RecordListItem<'a> for MinMax<'a> {
	fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			tag,
			min_coord: s.parse_at_offset16(data),
			max_coord: s.parse_at_offset16(data),
			feat_min_max: s.parse_array16()?,
			data,
		})
	}
}
impl<'a> MinMax<'a> {
	pub fn get_feat_min_max(&'a self, index: u16) -> Option<(Tag, Option<BaseCoord<'a>>, Option<BaseCoord<'a>>)> {
		self.get_feat_min_max_(self.feat_min_max.get(index)?)
	}
	pub fn get_feat_min_max_(&'a self, r: FeatMinMax) -> Option<(Tag, Option<BaseCoord<'a>>, Option<BaseCoord<'a>>)> {
		Some((
			r.tag,
			r.min_coord.and_then(|v| self.data.get(v.to_usize()..)).and_then(BaseCoord::parse),
			r.max_coord.and_then(|v| self.data.get(v.to_usize()..)).and_then(BaseCoord::parse),
		))
	}
}
impl<'a> core::fmt::Debug for MinMax<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("MinMax")
			.field("tag", &self.tag)
			.field("min_coord", &self.min_coord)
			.field("max_coord", &self.max_coord)
			// .field("feat_min_max", &self.feat_min_max.len())
			.field(
				"feat_min_max",
				&self
					.feat_min_max
					.into_iter()
					.map(|r| self.get_feat_min_max_(r))
					.flatten()
					.collect::<Vec<_>>(),
			)
			// .field("data", &self.data)
			.finish()
	}
}

#[derive(Debug, Clone, Copy)]
pub struct FeatMinMax {
	// Tag	featureTag	4-byte feature identification tag — must match feature tag in FeatureList.
	pub tag: Tag,
	// Offset16	minCoordOffset	Offset to BaseCoord table that defines the minimum extent value, from beginning of MinMax table (may be NULL).
	pub min_coord: Option<Offset16>,
	// Offset16	maxCoordOffset	Offset to BaseCoord table that defines the maximum extent value, from beginning of MinMax table (may be NULL).
	pub max_coord: Option<Offset16>,
}
impl FromData for FeatMinMax {
	const SIZE: usize = 8;
	fn parse(data: &[u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(Self {
			tag: s.read()?,
			min_coord: s.read()?,
			max_coord: s.read()?,
		})
	}
}

#[derive(Debug, Clone, Copy)]
pub enum BaseCoord<'a> {
	Format1 {
		// uint16	format	Format identifier — format = 1.
		// int16	coordinate	X or Y value, in design units.
		coordinate: i16,
	},
	Format2 {
		// uint16	format	Format identifier — format = 2.
		// int16	coordinate	X or Y value, in design units.
		// uint16	referenceGlyph	Glyph ID of control glyph.
		// uint16	baseCoordPoint	Index of contour point on the reference glyph.
		coordinate: i16,
		reference_glyph: GlyphId,
		base_coord_point: u16,
	},
	Format3 {
		// uint16	format	Format identifier — format = 3.
		// int16	coordinate	X or Y value, in design units.
		coordinate: i16,
		//Offset16	deviceOffset	Offset to Device table (non-variable font) / Variation Index table (variable font) for X or Y value, from beginning of BaseCoord table (may be NULL).
		device: Option<Device<'a>>,
	},
}
impl<'a> FromSlice<'a> for BaseCoord<'a> {
	fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let format: u16 = s.read()?;
		Some(match format {
			1 => Self::Format1 { coordinate: s.read()? },
			2 => Self::Format2 {
				coordinate: s.read()?,
				reference_glyph: s.read()?,
				base_coord_point: s.read()?,
			},
			3 => Self::Format3 {
				coordinate: s.read()?,
				device: s.parse_at_offset16(data),
			},
			_ => return None,
		})
	}
}
